<?php
/**
 * clase_zen_POA_Aspecto.php
 * @author Juan Belon
 * @access public
 * @copyright LGPL, GPL
 * @package zen_POA
 * @version 0.1.1
 * @uses zenphp FrameWork
 * @link http://csl2-zenphp.forja.rediris.es
 * @link http://www.zenphp.es
 * @link https://forja.rediris.es/projects/csl2-zenphp/
 * @magic 
 * TODO: Completar la Documentación de las clases de POA
 */
// +----------------------------------------------------------------------
// | zenphp.es
// +----------------------------------------------------------------------
require_once "clase_zen_POA_XML.php";
require_once "clase_zen_POA_Anotador.php";
require_once "clase_zen_POA_PuntoCorte.php";
require_once "clase_zen_POA_PuntoCorte_Auto.php";
require_once "clase_zen_POA_PuntoCorte_Propio.php";

class zen_POA_Aspecto
    {
    /**
    * @desc fichero XML a usar
    * @var str
    */
    var $_fichero_XML;
    /**
    * @desc Bandera
    * @var bool
    */
    var $_inicializado;
    /**
    * @desc última modificación realizada
    * @var str
    */
    var $_ultimaModificacion;
    /**
    * @desc Listado de puntos de corte definidos
    * @var array
    */
    var $_puntos_corte;

    /**
    * @desc Constructor de la clase
    * @param str $fichero_XML
    * @param bool $inicializar_aqui
    */
    function zen_POA_Aspecto($fichero_XML, $inicializar_aqui = true)
        {
        $this->_fichero_XML       =$fichero_XML;
        $this->_inicializado      =false;
        $this->_ultimaModificacion=filemtime($this->_fichero_XML);

        $this->_puntos_corte=array();

        // Por motivos de pruebas:
        $this->hash=md5(rand());

        // Inicializar? (la version compilada usa menos recursos)
        if ($inicializar_aqui === false)
            {
            $this->inicializar();
            }
        }

    /**
    * @desc Cargar un $fichero_XML y inicializarlo en la función
    * @param str $fichero_XML
    * @param bool $inicializar_aqui
    * @return zen_POA_Aspecto
    */
    function &cargar($fichero_XML, $inicializar_aqui = true)
        {
        // Este metodo implementa una versión modificada del Patrón Registro.
        // Añade la posibilidad de crear Instancias únicas de zen_POA_Aspecto.
        // Asi se previene el consumo extra de memoria del servidor.
        static $_instancias = array();

        // Generar Definición del Aspecto Xml (XAD)->código Hash
        $hash=md5(realpath($fichero_XML));

        if (!array_key_exists($hash, $_instancias))
            {
            $_instancias[$hash]=&new zen_POA_Aspecto($fichero_XML, $inicializar_aqui);
            }

        return $_instancias[$hash];
        }

    /**
    * @desc 
    */
    function insertarPuntoCorte(&$punto_corte)
        {
        $this->_puntos_corte[count($this->_puntos_corte)]=&$punto_corte;
        }

    /**
    * @desc 
    */
    function &obtenerPuntoCorte($i)
        {
        if ($this->_inicializado === false)
            {
            $this->inicializar();
            }

        if (array_key_exists($i, $this->_puntos_corte))
            {
            return $this->puntos_corte[$i];
            }

        return null;
        }

    /**
    * @desc 
    */
    function &obtenerPuntosDeCorte()
        {
        if ($this->_inicializado === false)
            {
            $this->inicializar();
            }

        return $this->_puntos_corte;
        }

    /**
    * @desc 
    */
    function obtenerFicheroXML() { return $this->_fichero_XML; }
    /**
    * @desc 
    */
    function obtenerInicializacion() { return $this->_inicializado; }
    /**
    * @desc 
    */
    function obtenerUltimaModificacion() { return $this->_ultimaModificacion; }

    /**
    * @desc 
    */
    function inicializar()
        {
        // Comprobar si existe el fichero:
        if (!file_exists($this->_fichero_XML))
            {
            trigger_error("<b>[Error de Aspecto]:</b> El fichero <b>" . $this->_fichero_XML . "</b> no existe!",
                          E_USER_ERROR);
            }

        // Obtener los contenidos:
        $contenido_fichero   =implode("", file($this->_fichero_XML));

        // Analizar el fichero XML con un mapa
        $lectorXML           =zen_POA_XML::construirDesdeCadena($contenido_fichero);

        // Cargar el directorio de origen de la Notación con el Anotador. Además cambia el directorio de trabajo actual (PWD)
        $anterior_dir_trabajo=getcwd();
        chdir(dirname(realpath($this->_fichero_XML)));

        // Comprobar el elemento de la raiz:
        if (strtolower($lectorXML->obtenerEtiqueta()) == "aspecto")
            {
            //Cada hijo es un zen_POA_ElementoXML
            foreach ($lectorXML->obtenerHijos()as $k => $v)
                {
                if ($v->obtenerEtiqueta() == "punto_corte")
                    {
                    // Comprobar atributos
                    $clase         =($v->obtenerAtributo("clase") === null) ? "" : $v->obtenerAtributo("clase");
                    $funcion       =($v->obtenerAtributo("funcion") === null) ? "" : $v->obtenerAtributo("funcion");
                    $nombre_clase  =($v->obtenerAtributo(
                                         "nombre_clase") === null) ? "" : $v->obtenerAtributo("nombre_clase");
                    $nombre_funcion=($v->obtenerAtributo(
                                         "nombre_funcion") === null) ? "" : $v->obtenerAtributo("nombre_funcion");

                    // Implemetar las anotaciones definidas por medio de fragmentos añadidos:
                    $anotador      =&new zen_POA_Anotador();

                    foreach ($v->obtenerHijos()as $c => $a)
                        {
                        // Añadir fragmento de Notación, dependiendo del tipo de origen (CDATA o un fichero externo)
                        if ($a->obtenerEtiqueta() == "notacion")
                            {
                            $incluido
                                =($a->obtenerAtributo("incluir") === null) ? "" : trim($a->obtenerAtributo("incluir"));

                            if ($incluido == "true")
                                {
                                $codigoNotacion=" require \"" . realpath(getcwd() . "/" . $a->obtenerAtributo(
                                                                                              "origen")) . "\"; ";
                                }
                            elseif ($incluido == "" || $incluido == "false")
                                {
                                $codigoNotacion=implode("", file($a->obtenerAtributo("origen")));

                                if (substr($codigoNotacion, 0, 2) == "<?")
                                    {
                                    // Recortar el archivo de principio a fin
                                    if (substr($codigoNotacion, 2, 3) == "php")
                                        {
                                        $codigoNotacion=substr($codigoNotacion, 5);
                                        }
                                    else
                                        {
                                        $codigoNotacion=substr($codigoNotacion, 2);
                                        }
                                    }
                                else
                                    {
                                    $codigoNotacion="?>" . $codigoNotacion;
                                    }

                                if (substr($codigoNotacion, -2, 2) == "?>")
                                    {
                                    $codigoNotacion=substr($codigoNotacion, 0, -2);
                                    }
                                else
                                    {
                                    $codigoNotacion.="<?php";
                                    }
                                }
                            else
                                {
                                trigger_error(
                                    "<b>[Error de Aspecto]:</b> Valor no definido: '<b>" . $incluido
                                        . "</b>' para la etiqueta notacion!",
                                    E_USER_ERROR);
                                }
                            }
                        else
                            {
                            $codigoNotacion=$a->obtenerValor();
                            }

                        $anotador->insertarContenido($codigoNotacion);
                        }

                    // Añadir zen_POA_PuntoCorte_Auto (automático) o PuntoCortePropio a la lista de Puntos de Corte
                    if ($v->obtenerAtributo("nombre") === null && $v->obtenerAtributo("auto") !== null)
                        {
                        $this->insertarPuntoCorte(
                            new zen_POA_PuntoCorte_Auto($anotador, $clase, $funcion, $v->obtenerAtributo("auto"),
                                                        $nombre_clase, $nombre_funcion));
                        }
                    else if ($v->obtenerAtributo("nombre") !== null && $v->obtenerAtributo("auto") === null)
                        {
                        $this->insertarPuntoCorte(
                            new zen_POA_PuntoCorte_Propio($anotador, $clase,
                                                          $funcion,      $v->obtenerAtributo("nombre"),
                                                          $nombre_clase, $nombre_funcion));
                        }
                    }
                }
            }

        // Re-configurar directorio de trabajo antes de usar el notificador
        chdir($anterior_dir_trabajo);

        // La bandera de carga cambia:
        $this->_inicializado=true;
        }


    /**
    * @desc Añadido
    */
    function &obtenerAnotadorDePuntoCortePropio($nombreClase, $nombreFuncion, $punto_corteName)
        {
        $anotador    =&new zen_POA_Anotador();

        $puntos_corte=&$this->obtenerPuntosDeCorte();
        $l           =count($puntos_corte);

        for ($i=0; $i < $l; $i++)
            {
            $punto_corte = &$puntos_corte[$i];

            if (is_a($punto_corte,
                     "zen_POA_PuntoCorte_Propio") && ($punto_corte->tieneNombreClase(
                                                          $nombreClase) || $punto_corte->tieneNombreClase(
                                                                               "")) && //Función de zen_POA_PuntoCorte
            ($punto_corte->tieneNombreFuncion(
                 $nombreFuncion) || $punto_corte->tieneNombreFuncion("")) &&           //Función de zen_POA_PuntoCorte
            $punto_corte->tieneNombre($punto_corteName))
                {                                                                      //Función de zen_POA_PuntoCorte_Propio
                if (!($punto_corte->tieneNombreClase_SinNombre(
                          $nombreClase) || $punto_corte->tieneNombreClase_SinNombre($nombreFuncion)))
                    {
                    $a=$punto_corte->obtenerAnotador();
                    $anotador->insertarContenido($a->obtenerContenido());
                    }
                else
                    {
                    $this->_comprobarCierresAtributos($punto_corte, $nombreClase, $nombreFuncion);
                    }
                }
            }

        return $anotador;
        }

    /**
    * @desc 
    */
    function &obtenerAnotadorDePuntoCorteAuto($nombreClase, $nombreFuncion, $punto_corte_auto)
        {
        $anotador    =&new zen_POA_Anotador();

        $puntos_corte=&$this->obtenerPuntosDeCorte();
        $l           =count($puntos_corte);

        for ($i=0; $i < $l; $i++)
            {
            $punto_corte = &$puntos_corte[$i];

            if (is_a($punto_corte,
                     "zen_POA_PuntoCorte_Auto") && ($punto_corte->tieneNombreClase(
                                                        $nombreClase) || $punto_corte->tieneNombreClase(
                                                                             "")) && ($punto_corte->tieneNombreFuncion(
                                                                                          $nombreFuncion)
                                                                                         || $punto_corte->tieneNombreFuncion(
                                                                                                "")))
                {
                if (!($punto_corte->tieneNombreClase_SinNombre(
                          $nombreClase) || $punto_corte->tieneNombreFuncion_SinNombre($nombreFuncion)))
                    {
                    if ($punto_corte->tieneAuto($punto_corte_auto))
                        {
                        $a=$punto_corte->obtenerAnotador();
                        $anotador->insertarContenido($a->obtenerContenido());
                        }

                    if ($punto_corte->tieneAuto("vistazo"))
                        {
                        $a=$punto_corte->obtenerAnotador();

                        eregi("(.*)[\t\s \n]procesar\(\);(.*)", $a->obtenerContenido(), $contenido);

                        echo "<pre>Data: " . htmlentities($a->obtenerContenido(),
                                                          ENT_QUOTES) . "\r\n\r\nPrintR: " . print_r($contenido,
                                                                                                     true) . "</pre>";

                        if ($punto_corte_auto == "antes")
                            {
                            $contenido=(isset($contenido[1]) ? $contenido[1] : $a->obtenerContenido());
                            }
                        elseif ($punto_corte_auto == "despues")
                            {
                            $contenido=(isset($contenido[2]) ? $contenido[2] : $a->obtenerContenido());
                            }

                        $anotador->insertarContenido($contenido);
                        }
                    }
                else
                    {
                    $this->_comprobarCierresAtributos($punto_corte, $nombreClase, $nombreFuncion);
                    }
                }
            }

        return $anotador;
        }


    /**
    * _comprobarCierresAtributos
    * @desc Comprueba que los cierres del XML están correctos
    * @param zen_POA_PuntoCorte &$punto_corte
    * @param str $nombreClase
    * @param str $nombreFuncion
    */
    function _comprobarCierresAtributos(&$punto_corte, $nombreClase, $nombreFuncion)
        {
        // Necesitamos comprobar si se cierran las etiquetas de clases y nombre de clases asi como de sus funciones y nombres de funciones
        if ($punto_corte->tieneNombreClase($nombreClase) && $punto_corte->tieneNombreClase_SinNombre($nombreClase))
            {
            trigger_error(
                "<b>[Error Aspecto]:</b> No se puede definir un Punto de Corte con el mismo nombre de clase [<b>"
                    . $nombreClase . "</b>], ver atributo: \"clase\" y \"nombre_class\".",
                E_USER_ERROR);
            }
        elseif ($punto_corte->tieneNombreFuncion(
                    $nombreFuncion) && $punto_corte->tieneNombreFuncion_SinNombre($nombreFuncion))
            {
            trigger_error(
                "<b>[Error Aspecto]:</b>No se puede definir un Punto de Corte con el mismo nombre de funcion [<b>"
                    . $nombreFuncion . "</b>],ver atributo: \"funcion\" y \"nombre_function\".",
                E_USER_ERROR);
            } // No hace nada mas si no concuerdan las etiquetas con su cierre!
        }
    }
?>